﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace Common
{
    public static class JWTHelper
    {
        /// <summary>
        /// 生成Token加密secretKey
        /// 对于这个密钥。有几种方式。
        /// 1.如下，直接定义一个。优点：后续webapi可以分布式，无限拓展，生成jwt统一一个地方(一个密钥)，
        /// 其他webapi统一一个密钥校验token。缺点:代码泄露不安全。
        /// 2.可以从.config文件的节点配置化加载，开发和生产隔离开。
        /// 3.程序运行时随机生成一个。缺点：每次重新发布程序，所有用户的token都会过期。还有4的缺点
        /// 4.程序运行时根据机器的信息（机器指纹等等）生成一个特定的。
        /// 优点：每次重新发布，都不会变化，也安全，同样的代码，不通机器生成又是不一样的密钥，保证的服务器的密钥是安全的。
        /// 缺点：只能单机部署站点。多站点的密钥不一样，导致token解析不了而认为token篡改。
        /// 5.使用第三方安全机制：可以实现webapi可以分布式拓展，也保证了代码泄露也不怕密钥泄露。这机制我也不知道是原理。
        /// 反正有这个东西。
        /// </summary>
        public static string SecretKey = "0ba2d0863b653700932e97e46363bd43";

        /// <summary>
        /// 生成JsonWebToken
        /// 这里用的jwt的思想，并不严格按他的规范
        /// </summary>
        /// <returns></returns>
        public static string Generate(JWTModel jwtModel)
        {
            //头
            var headerObj = new { alg = "HS256", typ = "JWT" };
            var headerStr = headerObj.ToJsonSerialize().Trim();
            var header = Convert.ToBase64String(Encoding.UTF8.GetBytes(headerStr));

            //体
            var payloadStr = jwtModel.ToJsonSerialize().Trim();
            var payload = Convert.ToBase64String(Encoding.UTF8.GetBytes(payloadStr));

            //密
            var signatureHSBytes = new HMACSHA256(Encoding.UTF8.GetBytes(SecretKey))
                .ComputeHash(Encoding.UTF8.GetBytes($"{header}.{payload}"));
            var signatureHS = BitConverter.ToString(signatureHSBytes).Replace("-", string.Empty);
            var signature = Convert.ToBase64String(Encoding.UTF8.GetBytes(signatureHS));

            //返回token
            string token = $"{header}.{payload}.{signature}";
            return token;
        }

        /// <summary>
        /// 校验Token
        /// </summary>
        /// <param name="token"></param>
        /// <returns>(错误原因,Token信息)</returns>
        public static (string, JWTModel) CheckToken(string token)
        {
            var tokens = token.Split('.');
            var header = tokens[0];
            var payload = tokens[1];
            var signature = tokens[2];

            var signatureHSBytes = new HMACSHA256(Encoding.UTF8.GetBytes(SecretKey))
                .ComputeHash(Encoding.UTF8.GetBytes($"{header}.{payload}"));
            var signatureHS = BitConverter.ToString(signatureHSBytes).Replace("-", string.Empty);
            var signatureGenerate = Convert.ToBase64String(Encoding.UTF8.GetBytes(signatureHS));

            //被篡改了
            if (signature != signatureGenerate)
                return ("token 被篡改", null);

            var payloadStr = Encoding.Default.GetString(Convert.FromBase64String(payload));
            var payloadMode = payloadStr.ToJsonDeserialize<JWTModel>();

            //身份过期
            if (payloadMode.expTime < DateTime.Now)
                return ("身份已过期", null);

            //...其他ip地址，浏览器指纹校验等等的验证就不拓展了
            return (string.Empty, payloadMode);
        }

        /// <summary>
        /// 获取用户
        /// </summary>
        /// <returns></returns>
        public static JWTModel GetUser()
        {
            var tokens = HttpContext.Current.Request.Headers.GetValues("User-Token");
            if (tokens != null && tokens.Any())
            {
                var checkResult = CheckToken(tokens[0]);
                return checkResult.Item2;
            }
            return null;
        }
    }
}
